package com.inspur.edp.common.expr.parser;

import com.inspur.edp.common.expr.ast.ASTContext;
import com.inspur.edp.common.expr.ast.CompileUnitParser;
import com.inspur.edp.common.expr.exception.ExpressException;
import com.inspur.edp.common.type.ArrayTypeRefer;
import com.inspur.edp.common.type.TypeRefer;
import com.inspur.edp.common.type.cache.TypeThreadCache;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lizhaorui
 * @date 2025/8/18
 * @description
 */
public class TypeAstParser {

    private static TypeAstParser instance;

    private Map<Class, TypeInternalVisitor> visitMap = new HashMap<>();

    public static TypeAstParser getInstance() {
        if (instance == null) {
            synchronized (TypeAstParser.class) {
                if (instance == null) {
                    TypeAstParser temp = new TypeAstParser();
                    temp.visitMap.put(CompileUnitParser.ReferTypeContext.class, new ReferTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.PrimitiveTypeContext.class, new PrimitiveTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.ArrayTypeContext.class, new ArrayTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.GenericTypeContext.class, new GenericTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.VoidTypeContext.class, new VoidTypeVisitor());
                    instance = temp;
                }
            }
        }
        return instance;
    }

    public static TypeRefer getTypeRefer(CompileUnitParser.TypeContext ctx, ASTContext context) {
        if (ctx == null) {
            return new TypeRefer("any");
        }
        TypeInternalVisitor visitor = getVisitor(ctx);
        return visitor.getTypeRefer(ctx, context);
    }

    public static String getTypeFullName(CompileUnitParser.TypeContext ctx, ASTContext context) {
        TypeInternalVisitor visitor = getVisitor(ctx);
        return visitor.getTypeFullName(ctx, context);
    }

    public static TypeInternalVisitor getVisitor(CompileUnitParser.TypeContext ctx) {
        if (ctx == null) {
            throw new ExpressException("ctx is null");
        }
        Class cls = ctx.getClass();
        TypeAstParser manager = TypeAstParser.getInstance();
        if (manager.visitMap.containsKey(cls)) {
            return manager.visitMap.get(cls);
        } else {
            throw new ExpressException("not support primitive type " + cls.getName());
        }
    }

    interface TypeInternalVisitor {

        TypeRefer getTypeRefer(CompileUnitParser.TypeContext ctx, ASTContext visitContext);

        String getTypeFullName(CompileUnitParser.TypeContext typeContext, ASTContext visitContext);
    }


    static class ReferTypeVisitor implements TypeInternalVisitor {
        @Override
        public TypeRefer getTypeRefer(CompileUnitParser.TypeContext ctx, ASTContext visitContext) {
            CompileUnitParser.ReferTypeContext context = (CompileUnitParser.ReferTypeContext) ctx;
            String typeName = NameAstParser.getNameStr(context.name());
            if (visitContext.containImportName(typeName)) {
                typeName = visitContext.getImportFullName(typeName);
            }
            if (visitContext.getGenericTypeNames().contains(typeName)) {
                return new TypeRefer(typeName);
            } else {
                TypeRefer typeRefer = TypeThreadCache.getType(new TypeRefer(typeName)).buildTypeRefer();
                return typeRefer;
            }
        }

        @Override
        public String getTypeFullName(CompileUnitParser.TypeContext typeContext, ASTContext visitContext) {
            CompileUnitParser.ReferTypeContext context = (CompileUnitParser.ReferTypeContext) typeContext;
            String typeName = context.name().getText();
            if (visitContext.containImportName(typeName)) {
                return visitContext.getImportFullName(typeName);
            } else {
                return typeName;
            }
        }
    }

    static class PrimitiveTypeVisitor implements TypeInternalVisitor {

        @Override
        public TypeRefer getTypeRefer(CompileUnitParser.TypeContext ctx, ASTContext visitContext) {
            CompileUnitParser.PrimitiveTypeContext context = (CompileUnitParser.PrimitiveTypeContext) ctx;
            String typeName = TypeAstParser.getPrimitiveTypeName(context.primitiveTypeEnum());
            return new TypeRefer(typeName);
        }

        @Override
        public String getTypeFullName(CompileUnitParser.TypeContext typeContext, ASTContext visitContext) {
            CompileUnitParser.PrimitiveTypeContext context = (CompileUnitParser.PrimitiveTypeContext) typeContext;
            String typeName = context.getText();
            if (visitContext.containImportName(typeName)) {
                return visitContext.getImportFullName(typeName);
            } else {
                return typeName;
            }
        }

    }

    static class ArrayTypeVisitor implements TypeInternalVisitor {

        @Override
        public TypeRefer getTypeRefer(CompileUnitParser.TypeContext ctx, ASTContext visitContext) {
            CompileUnitParser.ArrayTypeContext context = (CompileUnitParser.ArrayTypeContext) ctx;
            ArrayTypeRefer typeRefer = new ArrayTypeRefer("Array");
            TypeRefer arrayItemType = TypeAstParser.getTypeRefer(context.type(), visitContext);
            typeRefer.addGenericType(arrayItemType);
            return typeRefer;
        }

        @Override
        public String getTypeFullName(CompileUnitParser.TypeContext typeContext, ASTContext visitContext) {
            return "Array";
        }
    }

    static class GenericTypeVisitor implements TypeInternalVisitor {


        @Override
        public TypeRefer getTypeRefer(CompileUnitParser.TypeContext ctx, ASTContext visitContext) {
            CompileUnitParser.GenericTypeContext context = (CompileUnitParser.GenericTypeContext) ctx;
            TypeRefer typeRefer = TypeAstParser.getTypeRefer(context.type(0), visitContext);
            List<CompileUnitParser.TypeContext> genericTypes = context.type();
            if (genericTypes != null) {
                for (int i = 1; i < context.type().size(); i++) {
                    CompileUnitParser.TypeContext genericType = context.type(i);
                    TypeRefer genericTypeRefer = TypeAstParser.getTypeRefer(genericType, visitContext);
                    typeRefer.addGenericType(genericTypeRefer);
                }
            }
            return typeRefer;
        }

        @Override
        public String getTypeFullName(CompileUnitParser.TypeContext typeContext, ASTContext visitContext) {
            CompileUnitParser.GenericTypeContext context = (CompileUnitParser.GenericTypeContext) typeContext;
            return TypeAstParser.getTypeFullName(context.type(0), visitContext);
        }
    }

    static class VoidTypeVisitor implements TypeInternalVisitor {

        @Override
        public TypeRefer getTypeRefer(CompileUnitParser.TypeContext ctx, ASTContext typeContext) {
            return new TypeRefer("void");
        }

        @Override
        public String getTypeFullName(CompileUnitParser.TypeContext typeContext, ASTContext visitContext) {
            return "void";
        }
    }

    private static String getPrimitiveTypeName(CompileUnitParser.PrimitiveTypeEnumContext ctx) {
        if (ctx == null) {
            return null;
        }
        if (ctx instanceof CompileUnitParser.StringTypeContext) {
            CompileUnitParser.StringTypeContext context = (CompileUnitParser.StringTypeContext) ctx;
            return context.String().getText();
        }
        if (ctx instanceof CompileUnitParser.BooleanTypeContext) {
            CompileUnitParser.BooleanTypeContext context = (CompileUnitParser.BooleanTypeContext) ctx;
            return context.Boolean().getText();
        }

        if (ctx instanceof CompileUnitParser.IntTypeContext) {
            CompileUnitParser.IntTypeContext context = (CompileUnitParser.IntTypeContext) ctx;
            return context.Int().getText();
        }
        if (ctx instanceof CompileUnitParser.LongTypeContext) {
            CompileUnitParser.LongTypeContext context = (CompileUnitParser.LongTypeContext) ctx;
            return context.Long().getText();
        }
        if (ctx instanceof CompileUnitParser.FloatTypeContext) {
            CompileUnitParser.FloatTypeContext context = (CompileUnitParser.FloatTypeContext) ctx;
            return context.Float().getText();
        }
        if (ctx instanceof CompileUnitParser.DoubleTypeContext) {
            CompileUnitParser.DoubleTypeContext context = (CompileUnitParser.DoubleTypeContext) ctx;
            return context.Double().getText();
        }

        if (ctx instanceof CompileUnitParser.AnyTypeContext) {
            CompileUnitParser.AnyTypeContext context = (CompileUnitParser.AnyTypeContext) ctx;
            return context.Any().getText();
        }
        return null;
    }

}